Render As You FetchとThe five UI states
#React #設計 #宣言的UI #宣言的UIの設計レシピ
Render As You FetchパターンにおけるThe five UI statesをどう捉えるかという話
ユーザー体験を最適化するなら
Container/Presenterパターンで宣言的UIを構築するとよい
パターンごとにPresenterを用意すれば良いので設計もシンプルになる
The five UI states > Error state:エラーの状態もContainerで扱えるので見通しがいい
一般的なThe five UI states > Loading state:ロードしている状態の要件だけなら
Render As You FetchパターンのSuspenseで親にローディングを任せるとかでもいい
エラー表示も考えるならError Boundaryが必要
設計上のメリットが大きいのはRender As You Fetchパターン
スコープは小さいに越したことはないし、コード量も減る
親が子のクエリの詳細を知らないですむ
長期的な視点でReact > Concurrent Featuresの思想に乗っかれる
進化に対応できるし、設計を段階的に進められる
The five UI states > Loading state:ロードしている状態を最適化したいとなったらコンポーネントツリーにSuspense差し込むだけでいい
子の振る舞いは変わらないので、積極的にリファクタリングしていける
ロジックの後方互換性を保ったままViewを最適化できる
並列リクエストやキャッシュ先読みなどのパフォーマンスチューニングが容易
Core Web Vitalsに有効
設計判断のポイント
The five UI statesやCore Web Vitalsの要件
Skeleton UIの有無
非同期処理と幅、高さの指定をどこでハンドリングしたいか
Render As You FetchパターンでSkeleton UIを実装するのは苦しいように感じる
code:tsx
type ArticlesProps = {
height: number
width: number
}
// Suspenseを利用したSkeleton UI
const Loader = (props: ArticlesProps) => {
return (
<SomeErrorBoundary>
<React.Suspense fallback={<Skeleton height={props.height} width={props.width} />}>
<Articles {...props}>
</React.Suspense>
</ErrorBoundary>
)
}
const Articles = (props: ArticlesProps) => {
// render as you fetch
const articles = useArticles()
return (
// 高さ, 幅をSkeletonと一致させないとレイアウトシフトが起こる
<AwesomeAnimationBox height={props.height} width={props.width}>
{articles.map(/*=*/)}
</AwesomeAnimationBox>
)
}
export { Loader as Articles}
---
// 親ではこんな感じに
<Articles height={80} width={80}/>
koushisa.iconの所感
段階的に戦略を切り替えるとよさそう
最初
開発効率を高く実装コストを減らしたいならRender As You Fetchパターン
UI/UXに拘ったりSkeleton UIを使いたいなら、Container/Presenterパターン
Recoil > Loadable#632bb4c78660300000bec185
理由
プロダクト立ち上げ期に細かくハンドリングしたいモチベーションが経験上ない
サッと作ってシュッと捨てれる方が嬉しい
Render As You FetchパターンからContainer/Presenterパターンの移行は簡単
コンポーネントを増やすか、移動させるだけでいい
既存実装へのアドオンで済む
Container/PresenterパターンからRender As You Fetchパターンの移行は手間がかかる
機能は追加よりも変更したり削除するほうが難しいので